package com.example.yx.demo18;

import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class A18 {
    static class Aspect{
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("before2");
        }

        @After("execution(* foo())")
        public void after() {
            System.out.println("after");
        }

        @AfterReturning("execution(* foo())")
        public void afterRunning() {
            System.out.println("afterRunning");
        }

        public void afterThrowing() {
            System.out.println("afterThrowing");
        }

//        public Object around(ProceedingJoinPoint pjp) throws Throwable {
//
//        }

        static class Target {
            public void foo() {
                System.out.println("target foo");
            }
        }


        public static void main(String[] args) throws Throwable {
            SingletonAspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
            //1.高级切面转低级切面类
            List<Advisor> list = new ArrayList<>();
            for (Method method : Aspect.class.getDeclaredMethods()) {
                //不是环绕通知
                if (method.isAnnotationPresent(Before.class)) {
                    String expression = method.getAnnotation(Before.class).value();
                    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                    pointcut.setExpression(expression);

                    AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method,pointcut,factory);

                    Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
                    list.add(advisor);

                }
                //环绕通知
                else if (method.isAnnotationPresent(AfterReturning.class)) {
                    String expression = method.getAnnotation(AfterReturning.class).value();
                    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                    pointcut.setExpression(expression);

                    AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method,pointcut,factory);

                    Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
                    list.add(advisor);
                }
                else if (method.isAnnotationPresent(After.class)) {
                    String expression = method.getAnnotation(After.class).value();
                    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                    pointcut.setExpression(expression);

                    AspectJAfterAdvice advice = new AspectJAfterAdvice(method,pointcut,factory);

                    Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
                    list.add(advisor);
                }
            }
            for (Advisor advisor : list) {
                System.out.println(advisor);
            }

            /*
            @Before 前置通知会被转换为下面原始的AspectJMethodBeforeAdvice形式，该对象包含了如下信息
                a.通知代码从哪里来
                b.切点是什么
                c.通知对象如何创建，本例共用一个Aspect对象
            类似的通知还有：（下面的环绕通知是指实现了MethodInterceptor）
                1.AspectJAroundAdvice 环绕通知
                2.AspectJAfterReturningAdvice
                3.AspectJAfterThrowingAdvice 环绕通知
                4.AspectJAfterAdvice 环绕通知
         */

            //2.通知统一转换为环绕通知 MethodInterceptor
            /*
            before1--------------------------------------------------------|
                                                                            |
                before2--------------------------------|                    |
                                                       |                    |
                                  目标               advice2              advice1
                                                       |                    |
                after2---------------------------------|                    |
                                                                            |
             after1--------------------------------------------------------|                                                               |






             */
            Target target = new Target();
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.setTarget(target);
            proxyFactory.addAdvisors(list);

            System.out.println(">>>>>>>>>>>>>>>>>>>>>");
            List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
            for (Object o : methodInterceptorList) {
                System.out.println(o);
            }
            //3.创建并执行调用链 （多个环绕通知 + 目标）
//            MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
//                    null,target,Target.class.getMethod("foo"),new Object[0],Target.class,methodInterceptorList
//            );
//            methodInvocation.proceed();


        }




    }
}
